/*
 * Copyright (C) 2020  pigworlds
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
/*    $OpenBSD: if_iwnvar.h,v 1.39 2020/10/11 07:05:28 mpi Exp $    */

/*-
 * Copyright (c) 2007, 2008
 *    Damien Bergamini <damien.bergamini@free.fr>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
#include <net80211/ieee80211_ra.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_priv.h>

#include <IOKit/network/IOMbufMemoryCursor.h>
#include <IOKit/IODMACommand.h>

struct iwn_rx_radiotap_header {
    struct ieee80211_radiotap_header wr_ihdr;
    uint64_t    wr_tsft;
    uint8_t        wr_flags;
    uint8_t        wr_rate;
    uint16_t    wr_chan_freq;
    uint16_t    wr_chan_flags;
    int8_t        wr_dbm_antsignal;
    int8_t        wr_dbm_antnoise;
} __packed;

#define IWN_RX_RADIOTAP_PRESENT                        \
    ((1 << IEEE80211_RADIOTAP_TSFT) |                \
     (1 << IEEE80211_RADIOTAP_FLAGS) |                \
     (1 << IEEE80211_RADIOTAP_RATE) |                \
     (1 << IEEE80211_RADIOTAP_CHANNEL) |                \
     (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |            \
     (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE))

struct iwn_tx_radiotap_header {
    struct ieee80211_radiotap_header wt_ihdr;
    uint8_t        wt_flags;
    uint8_t        wt_rate;
    uint16_t    wt_chan_freq;
    uint16_t    wt_chan_flags;
} __packed;

#define IWN_TX_RADIOTAP_PRESENT                        \
    ((1 << IEEE80211_RADIOTAP_FLAGS) |                \
     (1 << IEEE80211_RADIOTAP_RATE) |                \
     (1 << IEEE80211_RADIOTAP_CHANNEL))

struct iwn_dma_info {
    IOBufferMemoryDescriptor* buffer;
    bus_addr_t        paddr;
    void             *vaddr;
    bus_size_t        size;
    IOBufferMemoryDescriptor *bmd;
    IODMACommand *cmd;
};

struct iwn_tx_data {
    bus_dmamap_t        map;
    bus_addr_t        cmd_paddr;
    bus_addr_t        scratch_paddr;
    mbuf_t        m;
    struct ieee80211_node    *ni;
    int totlen;

    /* A-MPDU subframes */
    int ampdu_txmcs;
    int ampdu_nframes;
};

struct iwn_tx_ring {
    struct iwn_dma_info    desc_dma;
    struct iwn_dma_info    cmd_dma;
    struct iwn_tx_desc    *desc;
    struct iwn_tx_cmd    *cmd;
    struct iwn_tx_data    data[IWN_TX_RING_COUNT];
    int            qid;
    int            queued;
    int            cur;
    int            read;
};

struct iwn_softc;

struct iwn_rx_data {
    mbuf_t   m;
    bus_dmamap_t    map;
};

struct iwn_rx_ring {
    struct iwn_dma_info    desc_dma;
    struct iwn_dma_info    stat_dma;
    uint32_t        *desc;
    struct iwn_rx_status    *stat;
    struct iwn_rx_data    data[IWN_RX_RING_COUNT];
    int            cur;
};

struct iwn_node {
    struct    ieee80211_node        ni;    /* must be the first */
    struct    ieee80211_amrr_node    amn;
    struct    ieee80211_ra_node    rn;
    uint16_t            disable_tid;
    uint8_t                id;
    uint8_t                ridx[IEEE80211_RATE_MAXSIZE];
    uint32_t            next_ampdu_id;
    int                lq_rate_mismatch;
};

struct iwn_calib_state {
    uint8_t        state;
#define IWN_CALIB_STATE_INIT    0
#define IWN_CALIB_STATE_ASSOC    1
#define IWN_CALIB_STATE_RUN    2

    u_int        nbeacons;
    uint32_t    noise[3];
    uint32_t    rssi[3];
    uint32_t    ofdm_x1;
    uint32_t    ofdm_mrc_x1;
    uint32_t    ofdm_x4;
    uint32_t    ofdm_mrc_x4;
    uint32_t    cck_x4;
    uint32_t    cck_mrc_x4;
    uint32_t    bad_plcp_ofdm;
    uint32_t    fa_ofdm;
    uint32_t    bad_plcp_cck;
    uint32_t    fa_cck;
    uint32_t    low_fa;
    uint8_t        cck_state;
#define IWN_CCK_STATE_INIT    0
#define IWN_CCK_STATE_LOFA    1
#define IWN_CCK_STATE_HIFA    2

    uint8_t        noise_samples[20];
    u_int        cur_noise_sample;
    uint8_t        noise_ref;
    uint32_t    energy_samples[10];
    u_int        cur_energy_sample;
    uint32_t    energy_cck;
};

struct iwn_calib_info {
    uint8_t        *buf;
    u_int        len;
};

struct iwn_fw_part {
    const uint8_t    *text;
    uint32_t    textsz;
    const uint8_t    *data;
    uint32_t    datasz;
};

struct iwn_fw_info {
    u_char            *data;
    size_t            size;
    struct iwn_fw_part    init;
    struct iwn_fw_part    main;
    struct iwn_fw_part    boot;
};

struct iwn_ops {
    int        (*load_firmware)(struct iwn_softc *);
    void        (*read_eeprom)(struct iwn_softc *);
    int        (*post_alive)(struct iwn_softc *);
    int        (*nic_config)(struct iwn_softc *);
    void        (*reset_sched)(struct iwn_softc *, int, int);
    void        (*update_sched)(struct iwn_softc *, int, int, uint8_t,
                uint16_t);
    int        (*get_temperature)(struct iwn_softc *);
    int        (*get_rssi)(const struct iwn_rx_stat *);
    int        (*set_txpower)(struct iwn_softc *, int);
    int        (*init_gains)(struct iwn_softc *);
    int        (*set_gains)(struct iwn_softc *);
    int        (*add_node)(struct iwn_softc *, struct iwn_node_info *,
                int);
    void        (*tx_done)(struct iwn_softc *, struct iwn_rx_desc *,
                struct iwn_rx_data *);
    void        (*ampdu_tx_start)(struct iwn_softc *,
                struct ieee80211_node *, uint8_t, uint16_t);
    void        (*ampdu_tx_stop)(struct iwn_softc *, uint8_t,
                uint16_t);
};

struct iwn_tx_ba {
    struct iwn_node *    wn;
};

struct iwn_softc {
    struct device        sc_dev;

    struct ieee80211com    sc_ic;
    int            (*sc_newstate)(struct ieee80211com *,
                    enum ieee80211_state, int);
    
    pci_intr_handle_t ih;

    struct ieee80211_amrr    amrr;
    uint8_t            fixed_ridx;

    bus_dma_tag_t        sc_dmat;

//    struct rwlock        sc_rwlock;
    u_int            sc_flags;
#define IWN_FLAG_HAS_5GHZ    (1 << 0)
#define IWN_FLAG_HAS_OTPROM    (1 << 1)
#define IWN_FLAG_CALIB_DONE    (1 << 2)
#define IWN_FLAG_USE_ICT    (1 << 3)
#define IWN_FLAG_INTERNAL_PA    (1 << 4)
#define IWN_FLAG_HAS_11N    (1 << 6)
#define IWN_FLAG_ENH_SENS    (1 << 7)
#define IWN_FLAG_ADV_BT_COEX    (1 << 8)
#define IWN_FLAG_BGSCAN        (1 << 9)
#define IWN_FLAG_SCANNING    (1 << 10)

    uint8_t         hw_type;

    struct iwn_ops        ops;
    const char        *fwname;
    const struct iwn_sensitivity_limits
                *limits;
    int            ntxqs;
    int            first_agg_txq;
    int            agg_queue_mask;
    int            ndmachnls;
    uint8_t            broadcast_id;
    int            rxonsz;
    int            schedsz;
    uint32_t        fw_text_maxsz;
    uint32_t        fw_data_maxsz;
    uint32_t        fwsz;
    bus_size_t        sched_txfact_addr;

    /* TX scheduler rings. */
    struct iwn_dma_info    sched_dma;
    uint16_t        *sched;
    uint32_t        sched_base;

    /* "Keep Warm" page. */
    struct iwn_dma_info    kw_dma;

    /* Firmware DMA transfer. */
    struct iwn_dma_info    fw_dma;

    /* ICT table. */
    struct iwn_dma_info    ict_dma;
    uint32_t        *ict;
    int            ict_cur;

    /* TX/RX rings. */
    struct iwn_tx_ring    txq[IWN5000_NTXQUEUES];
    struct iwn_rx_ring    rxq;

    bus_space_tag_t        sc_st;
    bus_space_handle_t    sc_sh;
    IOInterruptEventSource *sc_ih;
    pci_chipset_tag_t    sc_pct;
    pcitag_t        sc_pcitag;
    bus_size_t        sc_sz;
    int            sc_cap_off;    /* PCIe Capabilities. */

    CTimeout *     calib_to;
    int            calib_cnt;
    struct iwn_calib_state    calib;

    struct task        init_task;

    struct iwn_fw_info    fw;
    struct iwn_calib_info    calibcmd[5];
    uint32_t        errptr;

    uint8_t            bss_node_addr[IEEE80211_ADDR_LEN];

    struct iwn_rx_stat    last_rx_stat;
    int            last_rx_valid;
#define IWN_LAST_RX_VALID    0x01
#define IWN_LAST_RX_AMPDU    0x02
    struct iwn_ucode_info    ucode_info;
    struct iwn_rxon        rxon;
    uint32_t        rawtemp;
    int            temp;
    int            noise;
    uint32_t        qfullmsk;

    uint32_t        prom_base;
    struct iwn4965_eeprom_band
                bands[IWN_NBANDS];
    uint16_t        rfcfg;
    uint8_t            calib_ver;
    char            eeprom_domain[4];
    uint32_t        eeprom_crystal;
    int16_t            eeprom_temp;
    int16_t            eeprom_voltage;
    int16_t            eeprom_rawtemp;
    int8_t            maxpwr2GHz;
    int8_t            maxpwr5GHz;
    int8_t            maxpwr[IEEE80211_CHAN_MAX];
    int8_t            enh_maxpwr[35];

    uint8_t            reset_noise_gain;
    uint8_t            noise_gain;

    uint32_t        tlv_feature_flags;

    int32_t            temp_off;
    uint32_t        int_mask;
    uint8_t            ntxchains;
    uint8_t            nrxchains;
    uint8_t            txchainmask;
    uint8_t            rxchainmask;
    uint8_t            chainmask;

    int            sc_tx_timer;

    struct iwn_tx_ba    sc_tx_ba[IEEE80211_NUM_TID];

#if NBPFILTER > 0
    caddr_t            sc_drvbpf;

    union {
        struct iwn_rx_radiotap_header th;
        uint8_t    pad[IEEE80211_RADIOTAP_HDRLEN];
    } sc_rxtapu;
#define sc_rxtap    sc_rxtapu.th
    int            sc_rxtap_len;

    union {
        struct iwn_tx_radiotap_header th;
        uint8_t    pad[IEEE80211_RADIOTAP_HDRLEN];
    } sc_txtapu;
#define sc_txtap    sc_txtapu.th
    int            sc_txtap_len;
#endif
};
